#define DEBUG
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn=500000, maxk=18;

int min(int a, int b) {
  return a<b ? a : b;
}

int max(int a, int b) {
  return a>b ? a : b;
}

namespace SA {
  bool cmp(int x, int y, int temp[], int len, int n) {
    return temp[x]==temp[y] && (x+len>n ? y+len>n : (y+len<=n && temp[x+len]==temp[y+len]));
  }

  void makeSA(int n, char s[], int sa[], int rank[]) {
    static int buc[maxn+1], temp[maxn+1];
    for (int i=1; i<=n; i++) {
      rank[i] = s[i]-'a'+1;
      buc[rank[i]]++;
    }
    for (int i=1; i<=26; i++) {
      buc[i] += buc[i-1];
    }
    for (int i=1; i<=n; i++) {
      sa[buc[rank[i]]--] = i;
    }

    for (int len=1; len<n; len<<=1) {
      memset(buc, 0, sizeof buc);
      for (int i=1; i<=n; i++) {
        buc[rank[i]]++;
      }
      for (int i=1; i<=rank[sa[n]]; i++) {
        buc[i] += buc[i-1];
      }
      for (int i=n; i>0; i--) {
        if (sa[i]>len) {
          temp[buc[rank[sa[i]-len]]--] = sa[i]-len;
        }
      }
      for (int i=n; i>n-len; i--) {
        temp[buc[rank[i]]--] = i;
      }

      for (int i=1; i<=n; i++) {
        sa[i] = temp[i], temp[i] = rank[i];
      }
      for (int i=1; i<=n; i++) {
        rank[sa[i]] = rank[sa[i-1]]+!cmp(sa[i-1], sa[i], temp, len, n);
      }
    }
  }

  void makeHeight(int n, int rank[], int sa[], char s[], int height[][maxk+1]) {
    for (int i=1; i<=n; i++) {
      int t=0;
      if (rank[i]>1) {
        int y=sa[rank[i]-1];
        t = max(height[rank[i-1]][0]-1, 0);
        for (; i+t<=n && y+t<=n && s[i+t]==s[y+t]; t++);
      }
      height[rank[i]][0] = t;
    }
  }
}

class RMQ {
public:
  int logt[maxn+1], rmq[maxn+1][maxk+1];

  void initRMQ(int n) {
    logt[1] = 0;
    for (int i=2; i<=n; i++) {
      logt[i] = logt[i/2]+1;
    }

    for (int j=1; j<=logt[n]; j++) {
      for (int i=1; i<=n; i++) {
        if (i+(1<<(j-1))<=n) {
          rmq[i][j] = min(rmq[i][j-1], rmq[i+(1<<(j-1))][j-1]);
        } else {
          rmq[i][j] = rmq[i][j-1];
        }
      }
    }
  }

  int getMin(int l, int r) {
    int len=logt[r-l+1];
    return min(rmq[l][len], rmq[r-(1<<len)+1][len]);
  }

  int findLeft(int o, int v) {
    int ans=o+1;
    for (int l=1, r=o, mid; l<=r; ) {
      mid=(l+r)/2;
      if (getMin(mid, o)>=v) {
        r = mid-1;
        ans = mid;
      } else {
        l = mid+1;
      }
    }
    return ans;
  }

  int findRight(int o, int v, int n) {
    int ans=o-1;
    for (int l=o, r=n, mid; l<=r; ) {
      mid=(l+r)/2;
      if (getMin(o, mid)>=v) {
        l = mid+1;
        ans = mid;
      } else {
        r = mid-1;
      }
    }
    return ans;
  }
};

class ChairmanTree {
public:
  int l, r, v;
  ChairmanTree *left, *right;

  ChairmanTree(int l, int r) {
    this->l=l, this->r=r;
    v = 0, left=right=NULL;
  }

  void set(ChairmanTree *old, int t, int tv) {
    if (l==r) {
      v = tv;
    } else {
      int mid=(l+r)/2;
      if (t<=mid) {
        right = old ? old->right : NULL;
        left = new ChairmanTree(l, mid);
        left->set(old ? old->left : NULL, t, tv);
      } else {
        left = old ? old->left : NULL;
        right = new ChairmanTree(mid+1, r);
        right->set(old ? old->right : NULL, t, tv);
      }
      v = 0;
      if (left) v = max(v, left->v);
      if (right) v = max(v, right->v);
    }
  }

  int get(int tl, int tr) {
    if (l==tl && r==tr) {
      return v;
    } else {
      int mid=(l+r)/2, ret=0;
      if (tl<=mid && left) {
        ret = max(ret, left->get(max(tl, l), min(tr, mid)));
      }
      if (tr>mid && right) {
        ret = max(ret, right->get(max(tl, mid+1), min(tr, r)));
      }
      return ret;
    }
  }
};

int main() {
  freopen("brr.in", "r", stdin);
  freopen("brr.out", "w", stdout);

  static int sa[maxn+1], rank[maxn+1];
  static char s[maxn+2];
  scanf("%s", s+1);
  int n=strlen(s+1);

  static RMQ rmq;
  SA::makeSA(n, s, sa, rank);
  SA::makeHeight(n, rank, sa, s, rmq.rmq);
  rmq.initRMQ(n);

  static ChairmanTree *ct[maxn+2];
  ct[n+1] = new ChairmanTree(1, n);

  static int f[maxn+1];
  for (int i=n; i>0; i--) {
    for (f[i]=f[i+1]+1; f[i]>1; f[i]--) {
      //留前去后
      int l=rmq.findLeft(rank[i], f[i]-1)-1, r=rmq.findRight(rank[i]+1, f[i]-1, n);
      if (ct[i+f[i]]->get(l, r)>=f[i]-1) {
        break;
      }

      //留后去前
      l=rmq.findLeft(rank[i+1], f[i]-1)-1, r=rmq.findRight(rank[i+1]+1, f[i]-1, n);
      if (ct[i+f[i]]->get(l, r)>=f[i]-1) {
        break;
      }
    }
    ct[i] = new ChairmanTree(1, n);
    ct[i]->set(ct[i+1], rank[i], f[i]);
  }

  printf("%d", ct[1]->get(1, n));

  fclose(stdin);
  fclose(stdout);
  return 0;
}
